package com.rop.system.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rop.model.vo.LoginVo;
import com.rop.result.Result;
import com.rop.result.ResultCodeEnum;
import com.rop.system.custom.CustomerUser;
import com.rop.system.service.SysLoginLogService;
import com.rop.util.IpUtil;
import com.rop.util.JwtHelper;
import com.rop.util.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;

/**
 * @Title: TokenLoginFilter
 * @Author Rop
 * @Date 2023/5/12 11:58
 * @description: 认证过滤器
 */

public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;

    private SysLoginLogService sysLoginLogService;

    // 构造
    public TokenLoginFilter(AuthenticationManager authenticationManager,
                            RedisTemplate redisTemplate,
                            SysLoginLogService sysLoginLogService) {
        this.setAuthenticationManager(authenticationManager);
        this.setPostOnly(false);
        // 指定登录接口以及提交方式，可以指定任意路径
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login", "POST"));

        this.redisTemplate = redisTemplate;
        this.sysLoginLogService = sysLoginLogService;
    }

    /**
     * 获取用户名和密码，认证
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        try {
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);

            String username = loginVo.getUsername();
            String password = loginVo.getPassword();
            Authentication authentication = new UsernamePasswordAuthenticationToken(username, password);

            return this.getAuthenticationManager().authenticate(authentication);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 认证成功
     * @param request ""
     * @param response ""
     * @param chain ""
     * @param authResult ""
     * @throws IOException ""
     * @throws ServletException ""
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        // 获取认证对象
        CustomerUser customerUser = (CustomerUser) authResult.getPrincipal();

        // 生成token
        String id = customerUser.getSysUser().getId();
        String username = customerUser.getSysUser().getUsername();
        String token = JwtHelper.createToken(Long.valueOf(id), username);

        // 保存权限数据
        redisTemplate.opsForValue().set(customerUser.getUsername(), JSON.toJSONString(customerUser.getAuthorities()));

        // 记录登录日志
        Integer status = customerUser.getSysUser().getStatus();

        sysLoginLogService.recordLoginLog(username, IpUtil.getIpAddress(request), 1, "登录成功");

        // 返回
        HashMap<String, String> map = new HashMap<>();
        map.put("token", token);

        ResponseUtil.out(response, Result.ok(map));
    }

    // 认证失败
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        if (failed.getCause() instanceof RuntimeException) {
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.DATA_ERROR.getCode(), failed.getMessage()));
        } else {
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_MOBILE_ERROR));
        }
    }
}
